package com.springboot.mqtt.service;

import com.springboot.mqtt.config.MqttProperties;
import com.springboot.mqtt.model.MqttMessage;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * MQTT服务类
 * 
 * <p>提供MQTT消息发布和订阅功能的核心服务，封装了底层MQTT客户端操作，
 * 提供简单易用的API供应用程序使用。</p>
 * 
 * @author nehc
 * @version 1.0.0
 * @since 2023-04-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MqttService {

    private final MqttAsyncClient mqttClient;
    private final MqttProperties mqttProperties;

    /**
     * 初始化方法
     * 在服务启动时订阅默认主题
     */
    @PostConstruct
    public void init() {
        // 如果配置了默认主题，则自动订阅
        if (mqttProperties.getDefaultTopic() != null && !mqttProperties.getDefaultTopic().isEmpty()) {
            try {
                subscribe(mqttProperties.getDefaultTopic());
                log.info("已自动订阅默认主题: {}", mqttProperties.getDefaultTopic());
            } catch (MqttException e) {
                log.error("自动订阅默认主题失败: {}", e.getMessage(), e);
            }
        }
    }

    /**
     * 销毁方法
     * 在服务关闭时断开MQTT连接
     */
    @PreDestroy
    public void destroy() {
        try {
            if (mqttClient != null && mqttClient.isConnected()) {
                mqttClient.disconnect().waitForCompletion(mqttProperties.getCommandTimeout() * 1000L);
                log.info("MQTT客户端已断开连接");
            }
        } catch (MqttException e) {
            log.error("断开MQTT连接时出错: {}", e.getMessage(), e);
        }
    }

    /**
     * 检查客户端连接状态
     * 
     * @return 是否已连接
     */
    public boolean isConnected() {
        return mqttClient != null && mqttClient.isConnected();
    }

    /**
     * 重新连接到MQTT服务器
     * 
     * @return 重连操作的CompletableFuture
     */
    public CompletableFuture<Void> reconnect() {
        CompletableFuture<Void> future = new CompletableFuture<>();
        
        try {
            if (mqttClient == null) {
                future.completeExceptionally(new IllegalStateException("MQTT客户端未初始化"));
                return future;
            }
            
            if (mqttClient.isConnected()) {
                log.info("MQTT客户端已经连接，无需重连");
                future.complete(null);
                return future;
            }
            
            mqttClient.connect(new MqttConnectOptions())
                .setActionCallback(new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken token) {
                        log.info("MQTT客户端重连成功");
                        future.complete(null);
                    }

                    @Override
                    public void onFailure(IMqttToken token, Throwable exception) {
                        log.error("MQTT客户端重连失败: {}", exception.getMessage(), exception);
                        future.completeExceptionally(exception);
                    }
                });
        } catch (MqttException e) {
            log.error("MQTT重连过程中出错: {}", e.getMessage(), e);
            future.completeExceptionally(e);
        }
        
        return future;
    }

    /**
     * 发布消息
     * 
     * @param message MQTT消息对象
     * @return 发布操作的CompletableFuture
     */
    public CompletableFuture<Void> publish(MqttMessage message) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        
        try {
            if (!isConnected()) {
                throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
            }
            
            // 创建Paho消息对象
            org.eclipse.paho.client.mqttv3.MqttMessage mqttMessage = 
                    new org.eclipse.paho.client.mqttv3.MqttMessage(message.getPayload().getBytes(StandardCharsets.UTF_8));
            mqttMessage.setQos(message.getQos());
            mqttMessage.setRetained(message.isRetained());
            
            // 发布消息
            mqttClient.publish(message.getTopic(), mqttMessage)
                .setActionCallback(new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken token) {
                        log.debug("消息发布成功: 主题={}, 内容={}", message.getTopic(), message.getPayload());
                        future.complete(null);
                    }

                    @Override
                    public void onFailure(IMqttToken token, Throwable exception) {
                        log.error("消息发布失败: {}", exception.getMessage(), exception);
                        future.completeExceptionally(exception);
                    }
                });
        } catch (Exception e) {
            log.error("发布消息时出错: {}", e.getMessage(), e);
            future.completeExceptionally(e);
        }
        
        return future;
    }

    /**
     * 使用单独参数发布消息
     * 
     * @param topic 消息主题
     * @param payload 消息内容
     * @return 发布操作的CompletableFuture
     */
    public CompletableFuture<Void> publish(String topic, String payload) {
        MqttMessage message = new MqttMessage(topic, payload, mqttProperties.getDefaultQos(), false);
        return publish(message);
    }

    /**
     * 使用完整参数发布消息
     * 
     * @param topic 消息主题
     * @param payload 消息内容
     * @param qos 服务质量
     * @param retained 是否保留
     * @return 发布操作的CompletableFuture
     */
    public CompletableFuture<Void> publish(String topic, String payload, int qos, boolean retained) {
        MqttMessage message = new MqttMessage(topic, payload, qos, retained);
        return publish(message);
    }

    /**
     * 订阅主题
     * 
     * @param topic 要订阅的主题
     * @return 订阅成功的主题
     * @throws MqttException MQTT异常
     */
    public String subscribe(String topic) throws MqttException {
        return subscribe(topic, mqttProperties.getDefaultQos());
    }

    /**
     * 使用指定QoS订阅主题
     * 
     * @param topic 要订阅的主题
     * @param qos 服务质量
     * @return 订阅成功的主题
     * @throws MqttException MQTT异常
     */
    public String subscribe(String topic, int qos) throws MqttException {
        if (!isConnected()) {
            throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
        }

        mqttClient.subscribe(topic, qos).waitForCompletion(mqttProperties.getCommandTimeout() * 1000L);
        log.info("已订阅主题: {}, QoS: {}", topic, qos);
        return topic;
    }

    /**
     * 批量订阅主题
     * 
     * @param topics 要订阅的主题列表
     * @return 订阅成功的主题列表
     * @throws MqttException MQTT异常
     */
    public List<String> subscribe(List<String> topics) throws MqttException {
        if (!isConnected()) {
            throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
        }

        String[] topicArray = topics.toArray(new String[0]);
        int[] qosArray = new int[topics.size()];
        
        // 默认使用相同的QoS级别
        Arrays.fill(qosArray, mqttProperties.getDefaultQos());
        
        mqttClient.subscribe(topicArray, qosArray).waitForCompletion(mqttProperties.getCommandTimeout() * 1000L);
        log.info("已批量订阅主题: {}", topics);
        return topics;
    }

    /**
     * 取消订阅主题
     * 
     * @param topic 要取消订阅的主题
     * @throws MqttException MQTT异常
     */
    public void unsubscribe(String topic) throws MqttException {
        if (!isConnected()) {
            throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
        }

        mqttClient.unsubscribe(topic).waitForCompletion(mqttProperties.getCommandTimeout() * 1000L);
        log.info("已取消订阅主题: {}", topic);
    }

    /**
     * 批量取消订阅主题
     * 
     * @param topics 要取消订阅的主题列表
     * @throws MqttException MQTT异常
     */
    public void unsubscribe(List<String> topics) throws MqttException {
        if (!isConnected()) {
            throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
        }

        String[] topicArray = topics.toArray(new String[0]);
        mqttClient.unsubscribe(topicArray).waitForCompletion(mqttProperties.getCommandTimeout() * 1000L);
        log.info("已批量取消订阅主题: {}", topics);
    }
} 